﻿using System;
using System.Net;
using System.Net.Sockets;
using Fadd.Net;

namespace Fadd.IO.Consoles
{
    /// <summary>
    /// Implementation of a console channel using the <see cref="BinaryTransport{T}"/> class.
    /// </summary>
    public class BinaryChannel : IConsoleChannel
    {
        private readonly BinaryTransport<ConsolePacket> _transport;

        /// <summary>
        /// Initializes a new instance of the <see cref="BinaryChannel"/> class.
        /// </summary>
        /// <param name="socket">Established connection.</param>
        public BinaryChannel(Socket socket)
        {
            _transport = new BinaryTransport<ConsolePacket>(socket);
            _transport.ObjectReceived += OnPacket;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="BinaryChannel"/> class.
        /// </summary>
        /// <param name="endPoint">Host to connect to.</param>
        /// <param name="connectionPolicy">How to treat lost connections</param>
        public BinaryChannel(IPEndPoint endPoint, ConnectionPolicy connectionPolicy)
        {
            _transport = new BinaryTransport<ConsolePacket>(endPoint);
            _transport.ObjectReceived += OnPacket;
        }

        private void OnPacket(object sender, ObjectEventArgs<ConsolePacket> e)
        {
            if (e.Object.WrappedMessage is ConsoleOutput)
            {
                var output = (ConsoleOutput) e.Object.WrappedMessage;
                OutputReceived(this, new OutputEventArgs(output));
            }
            else if (e.Object.WrappedMessage is Command)
            {
                var cmd = (Command) e.Object.WrappedMessage;
                CommandReceived(this, new CommandEventArgs(cmd.Name, cmd.Arguments));
            }
            else if (e.Object.WrappedMessage is CommandResponse)
            {
                var response = (CommandResponse) e.Object.WrappedMessage;
                CommandResponseReceived(this, new CommandResponseEventArgs(response));
            }
        }

        #region IConsoleChannel Members

        /// <summary>
        /// A command was received from the other end.
        /// </summary>
        public event EventHandler<CommandEventArgs> CommandReceived = delegate{};

        /// <summary>
        /// Console output (something written to standard out at the remote endpoint) have been received
        /// </summary>
        public event EventHandler<OutputEventArgs> OutputReceived = delegate{};

        public event EventHandler<CommandResponseEventArgs> CommandResponseReceived = delegate { };

        /// <summary>
        /// Send a command enter in the console
        /// </summary>
        /// <param name="command">Command and it's arguments</param>
        /// <remarks>
        /// Commands are typically read from StandardIn in console windows.
        /// </remarks>
        public void Send(Command command)
        {
            _transport.Send(new ConsolePacket(command));
        }

        /// <summary>
        /// Send output to remote party
        /// </summary>
        /// <param name="output">Output from the console.</param>
        public void Send(ConsoleOutput output)
        {
            _transport.Send(new ConsolePacket(output));
        }

        #endregion

        #region IStartable Members

        /// <summary>
        /// Start component
        /// </summary>
        public void Start()
        {
            _transport.Start();
        }

        /// <summary>
        /// Stop component
        /// </summary>
        public void Stop()
        {
            _transport.Stop();
        }

        #endregion
    }
}